using System;
using System.Net;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.IO;
using System.Web.Services.Protocols;
using System.Data;
using dashCommerce.Net.com.paypal.www;

using Commerce.Common;

namespace Commerce.PayPal
{
    /// <summary>
    /// PayPal APIWrapper
    /// - updated 4/17/2008 - Paul Sterling, Motus Connect and Homax Group
    /// </summary>

    public class APIWrapper
    {

        #region Constants

        public const string DP_BN_ID = "motusconnect-dp";
        public const string EC_BN_ID = "motusconnect-ec";

        #endregion

        #region Required Properties
        private string _apiUserName;
        private string _apiPassword;
        private string _signature;
        private CurrencyCode _currencyCode = CurrencyCode.USD;
        private bool _isLive = false;
        private CurrencyCodeType ppCurrencyType = CurrencyCodeType.USD;


        public Commerce.Common.CurrencyCode CurrencyCode
        {
            get
            {
                return _currencyCode;
            }
        }

        public string APIUserName
        {
            get
            {
                return _apiUserName;
            }
        }

        public string APIPassword
        {
            get
            {
                return _apiPassword;
            }
        }

        public string Signature
        {
            get
            {
                return _signature;
            }
        }

        public bool IsLive
        {
            get
            {
                return _isLive;
            }
        }

        #endregion

        PayPalAPISoapBinding service;  //API SOAP
        PayPalAPIAASoapBinding serviceAA;  //API AA SOAP

        #region create

        public APIWrapper(string apiUserName, string apiPassword, string signature, CurrencyCode defaultCurrencyCode, bool isLive)
        {
            try
            {
                _apiUserName = apiUserName;
                _apiPassword = apiPassword;
                _signature = signature;

                if (!Enum.IsDefined(typeof(CurrencyCodeType), Enum.GetName(typeof(CurrencyCode), defaultCurrencyCode)))
                {
                    throw new ArgumentException(string.Format("Provided currency code is not a valid PayPalSvc.CurrencyCodeType. Currency Code: {0}.", defaultCurrencyCode), "defaultCurrencyCode");
                }

                ppCurrencyType = (CurrencyCodeType)Enum.Parse(typeof(CurrencyCodeType), Enum.GetName(typeof(CurrencyCode), defaultCurrencyCode), true);
                _currencyCode = defaultCurrencyCode;
                _isLive = isLive;

                service = new PayPalAPISoapBinding();
                service.Url = PayPalServiceUtility.GetPayPalServiceEndpoint(IsLive, true, true);
                service.RequesterCredentials = new CustomSecurityHeaderType();
                service.RequesterCredentials.Credentials = new UserIdPasswordType();
                service.RequesterCredentials.Credentials.Username = APIUserName;
                service.RequesterCredentials.Credentials.Password = APIPassword;
                service.RequesterCredentials.Credentials.Signature = Signature;
                service.RequesterCredentials.Credentials.Subject = string.Empty;

                serviceAA = new PayPalAPIAASoapBinding();
                serviceAA.Url = PayPalServiceUtility.GetPayPalServiceEndpoint(IsLive, true, true);
                serviceAA.RequesterCredentials = new CustomSecurityHeaderType();
                serviceAA.RequesterCredentials.Credentials = new UserIdPasswordType();
                serviceAA.RequesterCredentials.Credentials.Username = APIUserName;
                serviceAA.RequesterCredentials.Credentials.Password = APIPassword;
                serviceAA.RequesterCredentials.Credentials.Signature = Signature;
                serviceAA.RequesterCredentials.Credentials.Subject = string.Empty;

            }
            catch (Exception)
            {

                throw;
            }
        }

        #endregion

        #region Info APIs

        public string RefundTransaction(string TransactionID, decimal RefundAmount, string Memo, string currencyCode)
        {

            string sReturn = "";
            CurrencyCodeType currType = (CurrencyCodeType)StringToEnum(typeof(CurrencyCodeType), currencyCode);
            // Create the Refund Request
            RefundTransactionRequestType refundRequest = new RefundTransactionRequestType();
            BasicAmountType amount = new BasicAmountType();
            amount.currencyID = currType;

            amount.Value = RefundAmount.ToString();
            refundRequest.Memo = Memo;
            refundRequest.Amount = amount;


            refundRequest.RefundType = RefundType.Partial;
            refundRequest.TransactionID = TransactionID;
            refundRequest.Version = PayPalServiceUtility.PayPalAPIVersionNumber;

            RefundTransactionReq request = new RefundTransactionReq();
            request.RefundTransactionRequest = refundRequest;


            try
            {
                RefundTransactionResponseType response = service.RefundTransaction(request);

                string ErrorCheck = CheckErrors(response);
                if (ErrorCheck != "")
                {
                    sReturn = ("The transaction was not successful: " + ErrorCheck);
                }
                else
                {
                    sReturn = ("Success");
                }
            }
            catch (Exception x)
            {
                sReturn = "SSL Failure, the transaction did not go through. Error: " + x.Message;

            }
            return sReturn;

        }

        public string RefundTransaction(string TransactionID, bool DoFullRefund)
        {

            string sReturn = "";

            // Create the Refund Request
            RefundTransactionRequestType refundRequest = new RefundTransactionRequestType();
            BasicAmountType amount = new BasicAmountType();

            CurrencyCodeType currType = (CurrencyCodeType)this._currencyCode;
            amount.currencyID = currType;

            if (DoFullRefund)
            {
                refundRequest.RefundType = RefundType.Full;
            }
            else
            {
                refundRequest.RefundType = RefundType.Partial;
            }
            refundRequest.TransactionID = TransactionID;
            refundRequest.Version = PayPalServiceUtility.PayPalAPIVersionNumber;

            RefundTransactionReq request = new RefundTransactionReq();
            request.RefundTransactionRequest = refundRequest;

            try
            {
                RefundTransactionResponseType response = service.RefundTransaction(request);
                string ErrorCheck = CheckErrors(response);
                if (ErrorCheck != "")
                {
                    sReturn = ErrorCheck;
                }
                else
                {
                    sReturn = "Success";
                }
            }
            catch (Exception x)
            {
                sReturn = "SSL Failure, the transaction did not go through. Error: " + x.Message;
            }
            return sReturn;

        }

        public string GetTransactionDetail(string transactionID, string delimiter)
        {
            string sReturn = "";
            GetTransactionDetailsRequestType detailRequest = new GetTransactionDetailsRequestType();
            detailRequest.TransactionID = transactionID;
            detailRequest.Version = PayPalServiceUtility.PayPalAPIVersionNumber;
            GetTransactionDetailsReq request = new GetTransactionDetailsReq();
            request.GetTransactionDetailsRequest = detailRequest;

            GetTransactionDetailsResponseType response = service.GetTransactionDetails(request);

            sReturn = response.Ack.ToString() + "\n";

            string sErrors = this.CheckErrors(response);
            if (sErrors == string.Empty)
            {
                //build out the response
                StringBuilder sb = new StringBuilder();
                sb.Append("************** Payment Information ******************" + delimiter);

                //payment info
                PaymentInfoType payment = response.PaymentTransactionDetails.PaymentInfo;
                sb.Append("ReceiptID: " + payment.ReceiptID + delimiter);
                sb.Append("TransactionID: " + payment.TransactionID + delimiter);
                sb.Append("PaymentDate: " + payment.PaymentDate + delimiter);
                sb.Append("GrossAmount: " + GetAmountValue(payment.GrossAmount) + delimiter);
                sb.Append("SettleAmount: " + GetAmountValue(payment.SettleAmount) + delimiter);
                sb.Append("FeeAmount: " + GetAmountValue(payment.FeeAmount) + delimiter);
                sb.Append("TaxAmount: " + GetAmountValue(payment.TaxAmount) + delimiter);
                sb.Append("PaymentStatus: " + payment.PaymentStatus + delimiter);
                sb.Append("PaymentType: " + payment.PaymentType + delimiter);
                sb.Append("TransactionType: " + payment.TransactionType + delimiter);
                sb.Append(delimiter);
                //sReturn+=response.PaymentTransactionDetails.PaymentInfo.ToString();
                sb.Append("************** Buyer Information ******************" + delimiter);

                //receiver info
                ReceiverInfoType receiver = response.PaymentTransactionDetails.ReceiverInfo;
                sb.Append("Business: " + receiver.Business + delimiter);
                sb.Append("Receiver: " + receiver.Receiver + delimiter);
                sb.Append("ReceiverID: " + receiver.ReceiverID + delimiter);

                /*
                 * --- Uncomment this to report Auction and Subscription info
                sb.Append("************** Auction Information ******************"+delimiter);

                AuctionInfoType auction=new AuctionInfoType();
                sb.Append("BuyerID: "+auction.BuyerID+delimiter);
                sb.Append("ClosingDate: "+auction.ClosingDate+delimiter);
                sb.Append("ClosingDateSpecified: "+auction.ClosingDateSpecified+delimiter);
                sb.Append("multiItem: "+auction.multiItem+delimiter);

                sb.Append("************** Subscription Information ******************"+delimiter);

                SubscriptionInfoType sub=new SubscriptionInfoType();

                sb.Append("EffectiveDate: "+sub.EffectiveDate+delimiter);
                sb.Append("EffectiveDateSpecified: "+sub.EffectiveDateSpecified+delimiter);
                sb.Append("Password: "+sub.Password+delimiter);
                sb.Append("reattempt: "+sub.reattempt+delimiter);
                sb.Append("Recurrences: "+sub.Recurrences+delimiter);
                sb.Append("recurring: "+sub.recurring+delimiter);
                sb.Append("RetryTime: "+sub.RetryTime+delimiter);
                sb.Append("RetryTimeSpecified: "+sub.RetryTimeSpecified+delimiter);
                sb.Append("SubscriptionDate: "+sub.SubscriptionDate+delimiter);
                sb.Append("SubscriptionDateSpecified: "+sub.SubscriptionDateSpecified+delimiter);
                sb.Append("SubscriptionID: "+sub.SubscriptionID+delimiter);
                sb.Append("Terms: "+sub.Terms+delimiter);
                sb.Append("Username: "+sub.Username+delimiter);
                */
                sReturn = sb.ToString();
            }
            else
            {
                sReturn = sErrors;
            }
            return sReturn;
        }

        public DataTable GetTransactionSearch(DateTime StartDate, TransactionSearchParam param, string delimiter)
        {
            DataTable table = new DataTable("results");
            TransactionSearchRequestType transSearch = new TransactionSearchRequestType();

            // Set up the TransactionSearch
            TransactionSearchReq request = new TransactionSearchReq();
            transSearch.StartDate = StartDate;

            request.TransactionSearchRequest = new TransactionSearchRequestType();
            transSearch.Version = PayPalServiceUtility.PayPalAPIVersionNumber;
            transSearch.CurrencyCodeSpecified = false;
            transSearch.EndDateSpecified = false;
            transSearch.StatusSpecified = false;

            //set the params
            transSearch.StartDate = StartDate;
            transSearch.EndDate = param.EndDate;

            #region args list
            int args = 1;
            if (param.TransactionID != "")
            {
                transSearch.TransactionID = param.TransactionID;
                args++;
            }

            if (param.Amount != "")
            {
                transSearch.Amount = new BasicAmountType();
                transSearch.Amount.Value = param.Amount;
                args++;
            }
            if (param.PayerEmail != "")
            {
                transSearch.Payer = param.PayerEmail;
                args++;
            }

            if (param.Currency != "")
            {
                transSearch.CurrencyCodeSpecified = true;
                args++;
            }

            if (param.ItemNumber != "")
            {
                transSearch.AuctionItemNumber = param.ItemNumber;
                args++;
            }

            if (param.LastName != "")
            {
                transSearch.PayerName = new PersonNameType();
                transSearch.PayerName.LastName = param.LastName;
                args++;
            }

            if (param.FirstName != "")
            {
                transSearch.PayerName = new PersonNameType();
                transSearch.PayerName.FirstName = param.FirstName;
                args++;
            }

            if (param.PaymentStatus != "")
            {
                transSearch.StatusSpecified = true;
                args++;
            }

            if (param.PaymentType != "")
            {
                transSearch.TransactionClassSpecified = true;
                args++;
            }
            #endregion

            request.TransactionSearchRequest = transSearch;

            //if there are more than 0 args set, run the transaction
            if (args > 0)
            {
                //run the transactioon
                TransactionSearchResponseType response = service.TransactionSearch(request);

                if (response != null)
                {
                    if (response.PaymentTransactions != null)
                    {
                        //build the columns out
                        DataColumn cTransactionID = new DataColumn("TransactionID");
                        DataColumn cFeeAmount = new DataColumn("FeeAmount");
                        DataColumn cGrossAmount = new DataColumn("GrossAmount");
                        DataColumn cNetAmount = new DataColumn("NetAmount");
                        DataColumn cPayer = new DataColumn("Payer");
                        DataColumn cPayerDisplayName = new DataColumn("PayerDisplayName");
                        DataColumn cStatus = new DataColumn("Status");
                        DataColumn cTimestamp = new DataColumn("Timestamp");
                        DataColumn cType = new DataColumn("Type");

                        table.Columns.Add(cTransactionID);
                        table.Columns.Add(cFeeAmount);
                        table.Columns.Add(cGrossAmount);
                        table.Columns.Add(cNetAmount);
                        table.Columns.Add(cPayer);
                        table.Columns.Add(cPayerDisplayName);
                        table.Columns.Add(cStatus);
                        table.Columns.Add(cTimestamp);
                        table.Columns.Add(cType);

                        DataRow dr;
                        foreach (PaymentTransactionSearchResultType trans in response.PaymentTransactions)
                        {
                            dr = table.NewRow();
                            dr["TransactionID"] = trans.TransactionID;
                            dr["FeeAmount"] = GetAmountValue(trans.FeeAmount);
                            dr["GrossAmount"] = GetAmountValue(trans.GrossAmount);
                            dr["NetAmount"] = GetAmountValue(trans.NetAmount);
                            dr["Payer"] = trans.Payer;
                            dr["PayerDisplayName"] = trans.PayerDisplayName;
                            dr["Status"] = trans.Status;
                            dr["Timestamp"] = trans.Timestamp.ToLongDateString();
                            dr["Type"] = trans.Type.ToString();

                            table.Rows.Add(dr);
                        }
                    }
                }
            }
            else
            {
                throw new Exception("You must specify at least one search paramater");
            }
            return table;
        }

        public string RunTransactionSearch(DateTime StartDate, TransactionSearchParam param, string delimiter)
        {

            string sOut = "";
            TransactionSearchRequestType transSearch = new TransactionSearchRequestType();

            // Set up the TransactionSearch
            TransactionSearchReq request = new TransactionSearchReq();
            transSearch.StartDate = StartDate;

            request.TransactionSearchRequest = new TransactionSearchRequestType();
            transSearch.Version = PayPalServiceUtility.PayPalAPIVersionNumber;
            transSearch.CurrencyCodeSpecified = false;
            transSearch.EndDateSpecified = false;
            transSearch.StatusSpecified = false;

            //set the params
            transSearch.StartDate = StartDate;
            transSearch.EndDate = param.EndDate;

            #region args list
            int args = 1;
            if (param.TransactionID != "")
            {
                transSearch.TransactionID = param.TransactionID;
                args++;
            }

            if (param.Amount != "")
            {
                transSearch.Amount = new BasicAmountType();
                transSearch.Amount.Value = param.Amount;
                args++;
            }
            if (param.PayerEmail != "")
            {
                transSearch.Payer = param.PayerEmail;
                args++;
            }

            if (param.Currency != "")
            {
                transSearch.CurrencyCodeSpecified = true;
                args++;
            }

            if (param.ItemNumber != "")
            {
                transSearch.AuctionItemNumber = param.ItemNumber;
                args++;
            }

            if (param.LastName != "")
            {
                transSearch.PayerName = new PersonNameType();
                transSearch.PayerName.LastName = param.LastName;
                args++;
            }

            if (param.FirstName != "")
            {
                transSearch.PayerName = new PersonNameType();
                transSearch.PayerName.FirstName = param.FirstName;
                args++;
            }

            if (param.PaymentStatus != "")
            {
                transSearch.StatusSpecified = true;
                args++;
            }

            if (param.PaymentType != "")
            {
                transSearch.TransactionClassSpecified = true;
                args++;
            }
            #endregion

            request.TransactionSearchRequest = transSearch;

            //if there are more than 0 args set, run the transaction
            if (args > 0)
            {
                //run the transactioon
                TransactionSearchResponseType response = service.TransactionSearch(request);

                StringBuilder sb = new StringBuilder();
                sb.Append("Status: " + response.Ack.ToString() + delimiter);
                sb.Append("*********** Results ***************" + delimiter);
                if (response != null)
                {
                    sb.Append("Ack" + response.Ack + delimiter);
                    sb.Append("Version" + response.Version + delimiter);
                    sb.Append("Build" + response.Build + delimiter);
                    sb.Append("CorrelationID" + response.CorrelationID + delimiter);

                    if (response.TimestampSpecified)
                        sb.Append("Timestamp" + response.Timestamp + delimiter);
                    else
                        sb.Append("Timestamp Not specified" + delimiter);

                    //PaymentTransactionSearchResultType[] trans=new PaymentTransactionSearchResultType();

                    //sb.Append( "PaymentTransactions: "+response.PaymentTransactions.Length.ToString()+delimiter );
                    if (response.PaymentTransactions != null)
                    {
                        foreach (PaymentTransactionSearchResultType trans in response.PaymentTransactions)
                        {

                            sb.Append("TransactionID: " + trans.TransactionID + delimiter);
                            sb.Append("FeeAmount: " + GetAmountValue(trans.FeeAmount) + delimiter);
                            sb.Append("GrossAmount: " + GetAmountValue(trans.GrossAmount) + delimiter);
                            sb.Append("NetAmount: " + GetAmountValue(trans.NetAmount) + delimiter);
                            sb.Append("Payer: " + trans.Payer + delimiter);
                            sb.Append("PayerDisplayName: " + trans.PayerDisplayName + delimiter);
                            sb.Append("Status: " + trans.Status + delimiter);
                            sb.Append("Timestamp: " + trans.Timestamp.ToLongDateString() + delimiter);
                            sb.Append("Type: " + trans.Type.ToString() + delimiter);
                            sb.Append("--" + delimiter + delimiter);


                        }
                    }

                    sOut = sb.ToString();
                }
                else
                {
                    sOut = sb.ToString() + delimiter + "No Results!";
                }

            }
            else
            {

                throw new Exception("You must specify at least one search paramater");

            }

            return sOut;
        }

        #endregion

        #region Payment APIs

        public string SetExpressCheckout(string billToEmail, decimal dTotal, string returnUrl, string cancelUrl, bool useAddressOverride, Address shippingAddress)
        {

            dashCommerce.Net.com.paypal.www.SetExpressCheckoutReq setRequest = new SetExpressCheckoutReq();
            dashCommerce.Net.com.paypal.www.SetExpressCheckoutRequestDetailsType setDetails = new SetExpressCheckoutRequestDetailsType();
            setRequest.SetExpressCheckoutRequest = new SetExpressCheckoutRequestType();
            setRequest.SetExpressCheckoutRequest.Version = PayPalServiceUtility.PayPalAPIVersionNumber;

            if (useAddressOverride)
            {
                setDetails.AddressOverride = "1";
                //Required Stuff
                setDetails.Address = new AddressType();
                setDetails.Address.Name = shippingAddress.FirstName + " " + shippingAddress.LastName;
                setDetails.Address.Street1 = shippingAddress.Address1;
                setDetails.Address.CityName = shippingAddress.City;
                setDetails.Address.PostalCode = shippingAddress.Zip;
                setDetails.Address.Country = (CountryCodeType)Enum.Parse(typeof(CountryCodeType), shippingAddress.Country);
                setDetails.Address.CountrySpecified = true;
                //Not Required
                setDetails.Address.Street2 = shippingAddress.Address2;
                setDetails.Address.StateOrProvince = shippingAddress.StateOrRegion;
            }

            setDetails.BuyerEmail = billToEmail;
            //Currency currency = new Currency();
            setDetails.OrderTotal = GetBasicAmount(decimal.Round(dTotal, 2));  //currency.CurrencyDecimals));
            setDetails.ReturnURL = returnUrl;
            setDetails.CancelURL = cancelUrl;

            setRequest.SetExpressCheckoutRequest.SetExpressCheckoutRequestDetails = setDetails;
            dashCommerce.Net.com.paypal.www.SetExpressCheckoutResponseType setResponse = serviceAA.SetExpressCheckout(setRequest);

            string errors = this.CheckErrors(setResponse);
            string sOut = "";
            if (errors == string.Empty)
            {
                sOut = setResponse.Token;
            }
            else
            {
                sOut = errors;
            }
            return sOut;
        }

        public Commerce.Common.Address GetExpressCheckout(string token)
        {
            //PayerInfo payer = new PayerInfo();  // not used
            Commerce.Common.Address payer = new Commerce.Common.Address();

            dashCommerce.Net.com.paypal.www.GetExpressCheckoutDetailsReq req = new GetExpressCheckoutDetailsReq();
            dashCommerce.Net.com.paypal.www.GetExpressCheckoutDetailsRequestType requestType = new GetExpressCheckoutDetailsRequestType();

            requestType.Token = token;
            requestType.Version = PayPalServiceUtility.PayPalAPIVersionNumber;

            req.GetExpressCheckoutDetailsRequest = requestType;

            dashCommerce.Net.com.paypal.www.GetExpressCheckoutDetailsResponseType response = serviceAA.GetExpressCheckoutDetails(req);

            string errors = this.CheckErrors(response);
            if (errors == string.Empty)
            {
                string ack = response.Ack.ToString();
                payer.Email = response.GetExpressCheckoutDetailsResponseDetails.PayerInfo.Payer;
                payer.PayPalPayerID = response.GetExpressCheckoutDetailsResponseDetails.PayerInfo.PayerID;
                payer.FirstName = response.GetExpressCheckoutDetailsResponseDetails.PayerInfo.PayerName.FirstName;
                payer.LastName = response.GetExpressCheckoutDetailsResponseDetails.PayerInfo.PayerName.LastName;
                payer.Address1 = response.GetExpressCheckoutDetailsResponseDetails.PayerInfo.Address.Street1;
                payer.Address2 = response.GetExpressCheckoutDetailsResponseDetails.PayerInfo.Address.Street2;
                payer.City = response.GetExpressCheckoutDetailsResponseDetails.PayerInfo.Address.CityName;
                payer.StateOrRegion = response.GetExpressCheckoutDetailsResponseDetails.PayerInfo.Address.StateOrProvince;
                payer.Zip = response.GetExpressCheckoutDetailsResponseDetails.PayerInfo.Address.PostalCode;
                payer.PayPalToken = response.GetExpressCheckoutDetailsResponseDetails.Token;
                payer.Country = response.GetExpressCheckoutDetailsResponseDetails.PayerInfo.Address.CountryName;
            }
            else
            {
                payer.PayPalPayerID = errors;
            }
            return payer;
        }

        public Commerce.Common.Transaction DoExpressCheckout(Commerce.Common.Order order, bool AuthOnly)
        {
            try
            {
                //validate that the token and PayerID are applied to the BillingAddress
                if (String.IsNullOrEmpty(order.BillingAddress.PayPalPayerID))
                    throw new Exception("No payer ID set for the BillingAddress of the order");

                if (String.IsNullOrEmpty(order.BillingAddress.PayPalToken))
                    throw new Exception("No Token set for the BillingAddress of the order");

                dashCommerce.Net.com.paypal.www.DoExpressCheckoutPaymentReq checkoutRequest = new DoExpressCheckoutPaymentReq();
                DoExpressCheckoutPaymentRequestType checkoutReqType = new DoExpressCheckoutPaymentRequestType();
                DoExpressCheckoutPaymentRequestDetailsType checkoutDetails = new DoExpressCheckoutPaymentRequestDetailsType();
                PaymentDetailsType paymentDetails = new PaymentDetailsType();

                if (!AuthOnly)
                {
                    checkoutDetails.PaymentAction = PaymentActionCodeType.Sale;
                }
                else
                {
                    checkoutDetails.PaymentAction = PaymentActionCodeType.Authorization;
                }

                int roundTo = System.Globalization.CultureInfo.CurrentCulture.NumberFormat.CurrencyDecimalDigits;
                checkoutDetails.Token = order.BillingAddress.PayPalToken;
                checkoutDetails.PayerID = order.BillingAddress.PayPalPayerID;
                checkoutReqType.Version = PayPalServiceUtility.PayPalAPIVersionNumber;
                decimal dTotal = order.OrderTotal;

                paymentDetails.OrderTotal = GetBasicAmount(RoundIt(dTotal, roundTo));
                paymentDetails.ShippingTotal = this.GetBasicAmount(RoundIt(order.ShippingAmount, roundTo));
                paymentDetails.TaxTotal = this.GetBasicAmount(RoundIt(order.TaxAmount, roundTo));
                paymentDetails.Custom = order.OrderNumber;
                paymentDetails.ItemTotal = GetBasicAmount(RoundIt(order.CalculateSubTotal(), roundTo));
                //paymentDetails.OrderDescription = orderDescription;

                // tell PayPal were making the call
                paymentDetails.ButtonSource = EC_BN_ID;

                // load the payment items
                PaymentDetailsItemType item;

                int itemCount = order.Items.Count;

                PaymentDetailsItemType[] items = new PaymentDetailsItemType[itemCount];

                for (int i = 0; i < itemCount; i++)
                {
                    item = new PaymentDetailsItemType();
                    item.Name = order.Items[i].ProductName;
                    item.Number = order.Items[i].Sku;
                    item.Quantity = order.Items[i].Quantity.ToString();
                    item.Amount = GetBasicAmount(RoundIt(order.Items[i].PricePaid, roundTo));
                    items[i] = item;
                }

                paymentDetails.PaymentDetailsItem = items;
                checkoutRequest.DoExpressCheckoutPaymentRequest = checkoutReqType;
                checkoutRequest.DoExpressCheckoutPaymentRequest.DoExpressCheckoutPaymentRequestDetails = checkoutDetails;
                checkoutRequest.DoExpressCheckoutPaymentRequest.DoExpressCheckoutPaymentRequestDetails.PaymentDetails = paymentDetails;
                dashCommerce.Net.com.paypal.www.DoExpressCheckoutPaymentResponseType response = serviceAA.DoExpressCheckoutPayment(checkoutRequest);

                string errors = this.CheckErrors(response);
                //string sOut = "";  //not used
                Commerce.Common.Transaction trans = new Commerce.Common.Transaction();
                if (errors == string.Empty)
                {
                    trans.GatewayResponse = response.Ack.ToString();
                    trans.AuthorizationCode = response.DoExpressCheckoutPaymentResponseDetails.PaymentInfo.TransactionID;
                    trans.TransactionNotes = "PayPal CorrelationId: " + response.CorrelationID.ToString();
                }
                else
                {
                    trans.GatewayResponse = errors;
                }

                //return out the transactionID
                return trans;

            }
            catch (Exception)
            {
                throw;
            }
        }

        /// <summary>
        /// Converts a string to an enum value
        /// </summary>
        /// <param name="t">Type</param>
        /// <param name="Value">string</param>
        /// <returns>object</returns>
        static object StringToEnum(Type t, string Value)
        {
            object oOut = null;

            foreach (System.Reflection.FieldInfo fi in t.GetFields())
            {
                if (fi.Name == Value)
                {
                    oOut = fi.GetValue(null);    // We use null because?
                }
            }

            return oOut;
        }

        /// <summary>
        /// Executes the PayPal DoDirectCheckout API Method
        /// </summary>
        /// <param name="AuthOnly">Commerce.Common.Order</param>
        /// <param name="order">bool</param>
        /// <returns>Commerce.Common.Transaction</returns>
        public Commerce.Common.Transaction DoDirectCheckout(Commerce.Common.Order order, bool AuthOnly)
        {

            dashCommerce.Net.com.paypal.www.DoDirectPaymentReq req = new DoDirectPaymentReq();
            req.DoDirectPaymentRequest = new DoDirectPaymentRequestType();
            req.DoDirectPaymentRequest.Version = PayPalServiceUtility.PayPalAPIVersionNumber;

            DoDirectPaymentRequestDetailsType details = new DoDirectPaymentRequestDetailsType();
            details.CreditCard = new CreditCardDetailsType();
            details.CreditCard.CardOwner = new PayerInfoType();
            details.CreditCard.CardOwner.Address = new AddressType();
            details.CreditCard.CardOwner.PayerName = new PersonNameType();
            details.PaymentDetails = new PaymentDetailsType();

            // set the payer country
            // TODO:  does this relate to AVS?
            CountryCodeType payerCountry = CountryCodeType.US;
            if (order.BillingAddress.Country != "US")
            {
                try
                {
                    payerCountry = (CountryCodeType)StringToEnum(typeof(CountryCodeType), order.BillingAddress.Country);
                }
                catch
                {
                    payerCountry = CountryCodeType.US;
                }
            }

            details.CreditCard.CardOwner.Address.Country = payerCountry;
            details.CreditCard.CardOwner.PayerCountry = payerCountry;

            details.CreditCard.CardOwner.Address.CountrySpecified = true;
            details.CreditCard.CardOwner.Address.Street1 = order.BillingAddress.Address1;
            details.CreditCard.CardOwner.Address.Street2 = order.BillingAddress.Address2;
            details.CreditCard.CardOwner.Address.CityName = order.BillingAddress.City;
            details.CreditCard.CardOwner.Address.StateOrProvince = order.BillingAddress.StateOrRegion;
            details.CreditCard.CardOwner.Address.PostalCode = order.BillingAddress.Zip;

            details.CreditCard.CardOwner.PayerName.FirstName = order.BillingAddress.FirstName;
            details.CreditCard.CardOwner.PayerName.LastName = order.BillingAddress.LastName;

            // TODO: PVS - this seems to always use the locale for the app and not the payer's locale
            // - any reason this can't be set to 2 always...per PayPal's requirement?
            int roundTo = System.Globalization.CultureInfo.CurrentCulture.NumberFormat.CurrencyDecimalDigits;

            // TODO:  we need to do the currency conversion here - to USD for now
            // get the conversion rate for the currency
            
            // do the conversion


            decimal dTotal = Math.Round(order.CalculateSubTotal(), roundTo) + Math.Round(order.TaxAmount, roundTo) + Math.Round(order.ShippingAmount, roundTo);

            // currency type is set for each value
            // TODO:  can we have multiple currencies in OrderTotal?
            details.PaymentDetails.OrderTotal = GetBasicAmount(dTotal);
            details.PaymentDetails.ShippingTotal = this.GetBasicAmount(Math.Round(order.ShippingAmount, roundTo));
            details.PaymentDetails.TaxTotal = this.GetBasicAmount(Math.Round(order.TaxAmount, roundTo));
            details.PaymentDetails.Custom = order.OrderNumber;
            details.PaymentDetails.ItemTotal = GetBasicAmount(Math.Round(order.CalculateSubTotal(), roundTo));

            // tell PayPal who's making the call
            details.PaymentDetails.ButtonSource = DP_BN_ID;

            //credit card
            details.CreditCard.CreditCardNumber = order.CreditCardNumber;
            CreditCardTypeType ccType = CreditCardTypeType.Visa;

            switch (order.CreditCardType)
            {
                case Commerce.Common.CreditCardType.MasterCard:
                    ccType = CreditCardTypeType.MasterCard;
                    break;
                case Commerce.Common.CreditCardType.Amex:
                    ccType = CreditCardTypeType.Amex;
                    break;
                case Commerce.Common.CreditCardType.Discover:
                    ccType = CreditCardTypeType.Discover;
                    break;
            }

            details.CreditCard.CreditCardType = ccType;
            details.CreditCard.ExpMonth = Convert.ToInt16(order.CreditCardExpireMonth);
            details.CreditCard.ExpMonthSpecified = true;
            details.CreditCard.ExpYear = Convert.ToInt16(order.CreditCardExpireYear);
            details.CreditCard.ExpYearSpecified = true;
            details.CreditCard.CVV2 = order.CreditCardSecurityNumber;

            //set the user IP
            details.IPAddress = order.UserIP;

            //set the req var to details object
            req.DoDirectPaymentRequest.DoDirectPaymentRequestDetails = details;

            //sale type
            if (AuthOnly)
            {
                details.PaymentAction = PaymentActionCodeType.Authorization;
            }
            else
            {
                details.PaymentAction = PaymentActionCodeType.Sale;
            }

            //send the request and get the response
            DoDirectPaymentResponseType response = serviceAA.DoDirectPayment(req);
            Commerce.Common.Transaction trans = new Commerce.Common.Transaction();
            trans.OrderID = order.OrderID;

            // set the correlation id
            trans.TransactionNotes = response.CorrelationID.ToString();

            string errors = this.CheckErrors(response);

            if (errors == string.Empty)
            {
                trans.GatewayResponse = response.Ack.ToString();
                trans.AuthorizationCode = response.TransactionID;
                trans.CVV2Code = response.CVV2Code;
            }
            else
            {
                errors += " - CVVCode: " + response.CVV2Code.ToString() + " - AVSCode: " + response.AVSCode.ToString();
                trans.GatewayResponse = errors;
                throw new Exception(errors);
            }
            return trans;
        }

        #endregion

        #region Helper Functions

        decimal RoundIt(decimal dPrice, int roundTo)
        {
            return Math.Round(dPrice, roundTo);
        }

        string GetAmountValue(BasicAmountType amount)
        {

            //coerce it to a decimal
            decimal dAmount = decimal.Parse(amount.ToString());

            string sOut = "";
            try
            {
                sOut = dAmount.ToString("c");
            }
            catch
            {
                sOut = "--";
            }
            return sOut;
        }

        BasicAmountType GetBasicAmount(decimal amount)
        {
            BasicAmountType bAmount = new BasicAmountType();

            bAmount.Value = amount.ToString();

            // specify a currency type for this amount
            bAmount.currencyID = ppCurrencyType;

            return bAmount;
        }

        string CheckErrors(AbstractResponseType abstractResponse)
        {
            bool errorsExist = false;

            string errorList = "";
            // First, check the Obvious.  Make sure Ack is not Success
            if (!abstractResponse.Ack.Equals(AckCodeType.Success))
            {
                errorsExist = true;
            }
            // Check to make sure there is nothing in the Errors Collection
            if (abstractResponse.Errors != null)
            {
                if (abstractResponse.Errors.Length > 0)
                {
                    errorsExist = true;
                    // Do something with the errors
                    errorList = "ERROR: ";
                    for (int i = 0; i < abstractResponse.Errors.Length; i++)
                    {
                        // Access each error abstractResponse.Errors[i] and do something
                        errorList += abstractResponse.Errors[i].LongMessage + " (" + abstractResponse.Errors[i].ErrorCode + ")" + Environment.NewLine;
                    }
                }
            }
            return errorList;
        }

        public class TransactionSearchParam
        {
            public DateTime EndDate = DateTime.UtcNow;
            public string TransactionID = "";
            public string Amount = "";
            public string Currency = "";
            public string ItemNumber = "";
            public string PayerEmail = "";
            public string LastName = "";
            public string FirstName = "";
            public string Receiver = "";
            public string ReceiptID = "";
            public string PaymentStatus = "";
            public string PaymentType = "";

        }

    }
        #endregion


}
